package top.zhangjianyong.blog.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.zhangjianyong.blog.cache.ApiCache;
import top.zhangjianyong.blog.enums.RespCode;
import top.zhangjianyong.blog.entity.dto.BlogCommentTreeDTO;
import top.zhangjianyong.blog.entity.po.Blog;
import top.zhangjianyong.blog.entity.po.BlogComment;
import top.zhangjianyong.blog.mapper.BlogCommentMapper;
import top.zhangjianyong.blog.params.comment.*;
import top.zhangjianyong.blog.response.ApiResponse;
import top.zhangjianyong.blog.response.PageDTO;
import top.zhangjianyong.blog.service.BlogCommentLikeService;
import top.zhangjianyong.blog.service.BlogCommentService;
import top.zhangjianyong.blog.utils.DesensitizationUtil;

/**
 * 博客评论服务实现类
 *
 * @author zhangjianyong
 * @since 2024/12/19
 */
@Service
public class BlogCommentServiceImpl extends ServiceImpl<BlogCommentMapper, BlogComment>
    implements BlogCommentService {

  private final BlogCommentLikeService blogCommentLikeService;
  private final IdentifierGenerator identifierGenerator;

  public BlogCommentServiceImpl(
      @Lazy BlogCommentLikeService blogCommentLikeService,
      IdentifierGenerator identifierGenerator) {
    this.blogCommentLikeService = blogCommentLikeService;
    this.identifierGenerator = identifierGenerator;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public ApiResponse save(CommentSaveParam param) {
    BlogComment comment = new BlogComment();
    BeanUtils.copyProperties(param, comment);

    // 提前生成ID（使用雪花算法）
    Long commentId = identifierGenerator.nextId(comment).longValue();
    comment.setId(commentId);

    // 设置基本信息
    String ip = ApiCache.get().getIp();
    comment.setIp(ip);
    if (StrUtil.isBlank(comment.getNickname())) {
      comment.setNickname(DesensitizationUtil.ip(ip));
    }
    comment.setStatus(0); // 待审核
    comment.setLikeCount(0);
    comment.setReplyCount(0);
    comment.setTotalReplyCount(0);
    comment.setIsAuthor(0);
    comment.setIsTop(0);
    comment.setIsHot(0);
    comment.setIsDel(0);

    // 处理层级关系和路径设置
    if (param.parentId() != null) {
      BlogComment parent = getById(param.parentId());
      if (parent == null) return ApiResponse.fail(RespCode.BLOG_COMMENT_NOT_FUND);
      comment.setParentId(param.parentId());
      comment.setRootId(parent.getRootId() != null ? parent.getRootId() : parent.getId());
      comment.setLevel(parent.getLevel() + 1);
      comment.setPath(parent.getPath() + "/" + commentId);
      if (param.replyToUserId() != null) {
        comment.setReplyToUserId(param.replyToUserId());
        comment.setReplyToNickname(param.replyToNickname());
      }
    } else {
      comment.setLevel(0);
      comment.setRootId(commentId);
      comment.setPath(String.valueOf(commentId));
    }

    boolean saved = save(comment);

    if (saved && comment.getParentId() != null) {
      // 更新父评论的回复计数
      updateReplyCount(comment.getParentId());
      updateTotalReplyCount(comment.getRootId());
    }

    return saved ? ApiResponse.success() : ApiResponse.fail(RespCode.BLOG_COMMENT_SAVE_ERROR);
  }

  @Override
  public ApiResponse getCommentTree(CommentTreeParam param) {
    // 获取顶级评论
    Page<BlogComment> topComments =
        lambdaQuery()
            .eq(BlogComment::getBlogId, param.blogId())
            .isNull(BlogComment::getParentId)
            .eq(BlogComment::getStatus, 1)
            .eq(BlogComment::getIsDel, 0)
            .orderByDesc(BlogComment::getIsTop)
            .orderByAsc(BlogComment::getCreateTime)
            .page(new Page<>(param.pageNum(), param.pageSize()));

    List<BlogCommentTreeDTO> result = new ArrayList<>();

    for (BlogComment topComment : topComments.getRecords()) {
      BlogCommentTreeDTO dto = convertToTreeDTO(topComment);

      // 递归构建完整的评论树（限制最大深度为10层，防止性能问题）
      buildCommentTree(dto, param.childrenLimit(), 10);

      result.add(dto);
    }

    return ApiResponse.success(PageDTO.getPage(topComments, result));
  }

  /** 递归构建评论树 */
  private void buildCommentTree(BlogCommentTreeDTO parentDto, Integer childrenLimit, int maxDepth) {
    // 如果达到最大深度，停止递归
    if (maxDepth <= 0) {
      return;
    }

    // 获取直接子评论
    List<BlogComment> children =
        lambdaQuery()
            .eq(BlogComment::getParentId, parentDto.getId())
            .eq(BlogComment::getStatus, 1)
            .eq(BlogComment::getIsDel, 0)
            .orderByAsc(BlogComment::getCreateTime)
            .last(childrenLimit != null ? "LIMIT " + childrenLimit : "")
            .list();

    if (!children.isEmpty()) {
      List<BlogCommentTreeDTO> childrenDTOs = new ArrayList<>();

      for (BlogComment child : children) {
        BlogCommentTreeDTO childDto = convertToTreeDTO(child);

        // 递归构建子评论的子评论，深度减1
        buildCommentTree(childDto, childrenLimit, maxDepth - 1);

        childrenDTOs.add(childDto);
      }

      parentDto.setChildren(childrenDTOs);

      // 检查是否还有更多子评论
      long totalChildren =
          lambdaQuery()
              .eq(BlogComment::getParentId, parentDto.getId())
              .eq(BlogComment::getStatus, 1)
              .eq(BlogComment::getIsDel, 0)
              .count();

      parentDto.setChildrenTotal(totalChildren);
      if (childrenLimit != null) {
        parentDto.setHasMore(totalChildren > childrenLimit);
      } else {
        parentDto.setHasMore(false);
      }
    }
  }

  @Override
  public ApiResponse loadMoreChildren(LoadMoreChildrenParam param) {
    List<BlogComment> children =
        lambdaQuery()
            .eq(BlogComment::getParentId, param.parentId())
            .eq(BlogComment::getStatus, 1)
            .eq(BlogComment::getIsDel, 0)
            .orderByAsc(BlogComment::getCreateTime)
            .last("LIMIT " + param.offset() + ", " + param.limit())
            .list();

    List<BlogCommentTreeDTO> result = new ArrayList<>();

    for (BlogComment child : children) {
      BlogCommentTreeDTO childDto = convertToTreeDTO(child);

      // 递归构建子评论的完整树结构（限制最大深度）
      buildCommentTree(childDto, param.limit(), 10);

      result.add(childDto);
    }

    return ApiResponse.success(result);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public ApiResponse likeComment(CommentLikeParam param) {
    String visitorUuid = ApiCache.get().getVisitorUuid();
    if (StrUtil.isBlank(visitorUuid)) {
      return ApiResponse.fail("访客标识获取失败，请刷新页面重试");
    }

    boolean hasLiked = blogCommentLikeService.hasLiked(param.commentId(), visitorUuid);

    if (param.isLike()) {
      // 点赞
      if (hasLiked) {
        return ApiResponse.fail("您已经点过赞了");
      }
      blogCommentLikeService.addLike(param.commentId(), visitorUuid, null);
      // 更新评论点赞数
      BlogComment comment = getById(param.commentId());
      comment.setLikeCount(comment.getLikeCount() + 1);
      updateById(comment);
    } else {
      // 取消点赞
      if (!hasLiked) {
        return ApiResponse.fail("您还没有点赞");
      }
      blogCommentLikeService.removeLike(param.commentId(), visitorUuid);
      // 更新评论点赞数
      BlogComment comment = getById(param.commentId());
      comment.setLikeCount(Math.max(0, comment.getLikeCount() - 1));
      updateById(comment);
    }

    return ApiResponse.success();
  }

  @Override
  public ApiResponse getCommentStats(Long blogId) {
    long totalComments =
        lambdaQuery()
            .eq(BlogComment::getBlogId, blogId)
            .eq(BlogComment::getStatus, 1)
            .eq(BlogComment::getIsDel, 0)
            .count();

    long topLevelComments =
        lambdaQuery()
            .eq(BlogComment::getBlogId, blogId)
            .isNull(BlogComment::getParentId)
            .eq(BlogComment::getStatus, 1)
            .eq(BlogComment::getIsDel, 0)
            .count();

    Map<String, Object> stats =
        Map.of(
            "totalComments", totalComments,
            "topLevelComments", topLevelComments,
            "replyComments", totalComments - topLevelComments);

    return ApiResponse.success(stats);
  }

  @Override
  public ApiResponse pageForAdmin(CommentPageParam param) {
    MPJLambdaWrapper<BlogComment> wrapper =
        new MPJLambdaWrapper<>(BlogComment.class, "t")
            .selectAll(BlogComment.class)
            .selectAssociation(
                Blog.class, BlogCommentTreeDTO::getBlogTitle, map -> map.result(Blog::getTitle))
            .like(StrUtil.isNotBlank(param.keyword()), BlogComment::getContent, param.keyword())
            .or(
                StrUtil.isNotBlank(param.keyword()),
                w -> w.like(BlogComment::getNickname, param.keyword()))
            .or(
                StrUtil.isNotBlank(param.keyword()),
                w -> w.like(BlogComment::getEmail, param.keyword()))
            .eq(null != param.blogId(), BlogComment::getBlogId, param.blogId())
            .eq(null != param.status(), BlogComment::getStatus, param.status())
            .eq(BlogComment::getLevel, 0) // 只查询主评论，不查询回复
            .leftJoin(Blog.class, Blog::getId, BlogComment::getBlogId)
            .orderByDesc(BlogComment::getCreateTime);

    // 如果过滤新回复，添加子查询条件
    if (param.reply() != null) {
      if (param.reply() == 1) {
        // 查询有新回复的评论
        wrapper.exists(
            "SELECT 1 FROM blog_comment bc WHERE bc.root_id = t.id AND bc.status = 0 AND bc.is_del = 0 AND bc.id != t.id");
      } else {
        // 查询没有新回复的评论
        wrapper.notExists(
            "SELECT 1 FROM blog_comment bc WHERE bc.root_id = t.id AND bc.status = 0 AND bc.is_del = 0 AND bc.id != t.id");
      }
    }

    Page<BlogCommentTreeDTO> page =
        baseMapper.selectJoinPage(
            new Page<>(param.pageNum(), param.pageSize()), BlogCommentTreeDTO.class, wrapper);

    // 设置格式化时间和新回复状态
    for (BlogCommentTreeDTO dto : page.getRecords()) {
      dto.setCreateTimeFormat(formatTime(dto.getCreateTime()));

      // 检查是否有新回复（待审核的回复）
      long newReplyCount =
          lambdaQuery()
              .eq(BlogComment::getRootId, dto.getId())
              .eq(BlogComment::getStatus, 0) // 待审核状态
              .eq(BlogComment::getIsDel, 0)
              .ne(BlogComment::getId, dto.getId()) // 排除自己
              .count();

      dto.setReply(newReplyCount > 0 ? 1 : 0);
    }

    return ApiResponse.success(page);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public ApiResponse changeStatus(CommentStatusChangeParam param) {
    BlogComment comment = getById(param.id());
    if (comment == null) {
      return ApiResponse.fail(RespCode.BLOG_COMMENT_NOT_FUND);
    }

    comment.setStatus(param.status());
    boolean updated = updateById(comment);

    return updated
        ? ApiResponse.success()
        : ApiResponse.fail(RespCode.BLOG_COMMENT_STATUS_CHANGE_FAIL);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public ApiResponse toggleTop(Long commentId) {
    BlogComment comment = getById(commentId);
    if (comment == null) {
      return ApiResponse.fail(RespCode.BLOG_COMMENT_NOT_FUND);
    }

    comment.setIsTop(comment.getIsTop() == 1 ? 0 : 1);
    boolean updated = updateById(comment);

    return updated ? ApiResponse.success() : ApiResponse.fail("操作失败");
  }

  @Override
  public ApiResponse get(CommentGetParam param) {
    BlogComment comment = getById(param.id());
    if (comment == null) {
      return ApiResponse.fail(RespCode.BLOG_COMMENT_NOT_FUND);
    }

    BlogCommentTreeDTO dto = convertToTreeDTO(comment);
    return ApiResponse.success(dto);
  }

  @Override
  public long countByUser(Long userId) {
    Long count = lambdaQuery().eq(BlogComment::getStatus, 1).eq(BlogComment::getIsDel, 0).count();
    return count == null ? 0 : count;
  }

  /** 更新直接回复计数 */
  private void updateReplyCount(Long parentId) {
    long count =
        lambdaQuery()
            .eq(BlogComment::getParentId, parentId)
            .eq(BlogComment::getStatus, 1)
            .eq(BlogComment::getIsDel, 0)
            .count();

    BlogComment parent = new BlogComment();
    parent.setId(parentId);
    parent.setReplyCount((int) count);
    updateById(parent);
  }

  /** 更新总回复计数 */
  private void updateTotalReplyCount(Long rootId) {
    long count =
        lambdaQuery()
            .eq(BlogComment::getRootId, rootId)
            .ne(BlogComment::getId, rootId)
            .eq(BlogComment::getStatus, 1)
            .eq(BlogComment::getIsDel, 0)
            .count();

    BlogComment root = new BlogComment();
    root.setId(rootId);
    root.setTotalReplyCount((int) count);
    updateById(root);
  }

  /** 转换为树形DTO */
  private BlogCommentTreeDTO convertToTreeDTO(BlogComment comment) {
    BlogCommentTreeDTO dto = new BlogCommentTreeDTO();
    BeanUtils.copyProperties(comment, dto);
    dto.setChildren(new ArrayList<>());
    dto.setCreateTimeFormat(formatTime(comment.getCreateTime()));
    dto.setRelativeTime(getRelativeTime(comment.getCreateTime()));

    // 检查是否已点赞
    String visitorUuid = ApiCache.get().getVisitorUuid();
    if (StrUtil.isNotBlank(visitorUuid)) {
      dto.setIsLiked(blogCommentLikeService.hasLiked(comment.getId(), visitorUuid));
    } else {
      dto.setIsLiked(false);
    }

    return dto;
  }

  /** 格式化时间 */
  private String formatTime(LocalDateTime dateTime) {
    return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
  }

  /** 获取相对时间 */
  private String getRelativeTime(LocalDateTime dateTime) {
    LocalDateTime now = LocalDateTime.now();
    long minutes = java.time.Duration.between(dateTime, now).toMinutes();

    if (minutes < 1) {
      return "刚刚";
    } else if (minutes < 60) {
      return minutes + "分钟前";
    } else if (minutes < 24 * 60) {
      return (minutes / 60) + "小时前";
    } else if (minutes < 30 * 24 * 60) {
      return (minutes / (24 * 60)) + "天前";
    } else {
      return formatTime(dateTime);
    }
  }

  @Override
  public ApiResponse getCommentTreeForAdmin(CommentGetParam param) {
    BlogComment comment = getById(param.id());
    if (comment == null) {
      return ApiResponse.fail(RespCode.BLOG_COMMENT_NOT_FUND);
    }

    // 如果这是一个回复，找到根评论
    Long rootId = comment.getRootId() != null ? comment.getRootId() : comment.getId();
    BlogComment rootComment = getById(rootId);

    if (rootComment == null) {
      return ApiResponse.fail(RespCode.BLOG_COMMENT_NOT_FUND);
    }

    // 构建根评论的树形结构
    BlogCommentTreeDTO rootDto = convertToTreeDTOForAdmin(rootComment);

    // 递归构建完整的评论树（包含所有状态的评论）
    buildCommentTreeForAdmin(rootDto, 10);

    return ApiResponse.success(rootDto);
  }

  /** 递归构建管理后台评论树（包含所有状态） */
  private void buildCommentTreeForAdmin(BlogCommentTreeDTO parentDto, int maxDepth) {
    // 如果达到最大深度，停止递归
    if (maxDepth <= 0) {
      return;
    }

    // 获取所有直接子评论（包含所有状态）
    List<BlogComment> children =
        lambdaQuery()
            .eq(BlogComment::getParentId, parentDto.getId())
            .eq(BlogComment::getIsDel, 0)
            .orderByAsc(BlogComment::getCreateTime)
            .list();

    if (!children.isEmpty()) {
      List<BlogCommentTreeDTO> childrenDTOs = new ArrayList<>();

      for (BlogComment child : children) {
        BlogCommentTreeDTO childDto = convertToTreeDTOForAdmin(child);

        // 递归构建子评论的子评论，深度减1
        buildCommentTreeForAdmin(childDto, maxDepth - 1);

        childrenDTOs.add(childDto);
      }

      parentDto.setChildren(childrenDTOs);
      parentDto.setChildrenTotal((long) children.size());
      parentDto.setHasMore(false);
    }
  }

  /** 转换为管理后台树形DTO（不检查点赞状态） */
  private BlogCommentTreeDTO convertToTreeDTOForAdmin(BlogComment comment) {
    BlogCommentTreeDTO dto = new BlogCommentTreeDTO();
    BeanUtils.copyProperties(comment, dto);
    dto.setChildren(new ArrayList<>());
    dto.setCreateTimeFormat(formatTime(comment.getCreateTime()));
    dto.setRelativeTime(getRelativeTime(comment.getCreateTime()));
    dto.setIsLiked(false); // 管理后台不需要点赞状态

    return dto;
  }
}
